From 7c23a46f497757994503bc00172de83d7a1c37fd Mon Sep 17 00:00:00 2001
From: Balazs Scheidler <bazsi77@gmail.com>
Date: Wed, 10 Mar 2021 07:34:18 +0100
Subject: [PATCH 1/2] virt-manager: move window resizing from details/console
 to vmwindow

This is in preparation for a more involved UI logic change where the size of
the window needs to be decoupled from the "full-screen" mode of the console.

Signed-off-by: Balazs Scheidler <bazsi77@gmail.com>
---
 virtManager/details/console.py | 2 --
 virtManager/vmwindow.py        | 6 ++++++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/virtManager/details/console.py b/virtManager/details/console.py
index 18f9ddd91..80d015d50 100644
--- a/virtManager/details/console.py
+++ b/virtManager/details/console.py
@@ -556,12 +556,10 @@ def _leave_fullscreen(self, ignore=None):
     def _change_fullscreen(self, do_fullscreen):
         if do_fullscreen:
             self._in_fullscreen = True
-            self.topwin.fullscreen()
             self._overlay_toolbar_fullscreen.timed_revealer.force_reveal(True)
         else:
             self._in_fullscreen = False
             self._overlay_toolbar_fullscreen.timed_revealer.force_reveal(False)
-            self.topwin.unfullscreen()
 
         self._sync_scaling_with_display()
 
diff --git a/virtManager/vmwindow.py b/virtManager/vmwindow.py
index 7783ead4e..2c2bdf35b 100644
--- a/virtManager/vmwindow.py
+++ b/virtManager/vmwindow.py
@@ -650,6 +650,12 @@ def _scaling_ui_changed_cb(self, src):
     def _fullscreen_changed_cb(self, src):
         do_fullscreen = src.get_active()
         self.widget("control-fullscreen").set_active(do_fullscreen)
+
+        if do_fullscreen:
+            self.topwin.fullscreen()
+        else:
+            self.topwin.unfullscreen()
+
         self._console.vmwindow_set_fullscreen(do_fullscreen)
 
         self.widget("details-menubar").set_visible(not do_fullscreen)

From e4f35dbb22972b8be63fb0034cba1065bee3e308 Mon Sep 17 00:00:00 2001
From: Balazs Scheidler <bazsi77@gmail.com>
Date: Wed, 10 Mar 2021 07:37:49 +0100
Subject: [PATCH 2/2] virt-manager: implement undecorated window mode

I have a 4k monitor and I have a VM that uses 1920x1080 resolution, which
I would like to lay out in the top-right corner of my monitor.

The problem is that as long as the window decoration, menubar and toolbar
is visible, 1920x1080 does not fit in the 1/4th of my monitor.

Also, the current virtual VGA driver for Windows does not support
non-standard resolutions like VirtualBox, thus I am either seeing a lot of
"black" space around my virtual desktop, or I would have to increase
the size of my virt-manager window.

None of this is appealing, so this patch implements a "windowed" full screen
mode, which is basically an undecorated window, without a menu or toolbar.

The same overlay is available at the top of the window than what is available
in full screen.

Signed-off-by: Balazs Scheidler <bazsi77@gmail.com>
---
 ui/vmwindow.ui          | 15 ++++++++
 virtManager/vmwindow.py | 79 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 81 insertions(+), 13 deletions(-)

diff --git a/ui/vmwindow.ui b/ui/vmwindow.ui
index 44afa55c7..db052a599 100644
--- a/ui/vmwindow.ui
+++ b/ui/vmwindow.ui
@@ -182,6 +182,21 @@
                         <signal name="activate" handler="on_details_menu_view_fullscreen_activate" swapped="no"/>
                       </object>
                     </child>
+                    <child>
+                      <object class="GtkCheckMenuItem" id="details-menu-view-undecorated-window">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Display in _Undecorated Window</property>
+                        <property name="use_underline">True</property>
+                        <signal name="activate" handler="on_details_menu_view_undecorated_window_activate" swapped="no"/>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparatorMenuItem">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                      </object>
+                    </child>
                     <child>
                       <object class="GtkMenuItem" id="detains-menu-view-size-to-vm">
                         <property name="visible">True</property>
diff --git a/virtManager/vmwindow.py b/virtManager/vmwindow.py
index 2c2bdf35b..31e1d893e 100644
--- a/virtManager/vmwindow.py
+++ b/virtManager/vmwindow.py
@@ -22,6 +22,9 @@
  DETAILS_PAGE_CONSOLE,
  DETAILS_PAGE_SNAPSHOTS) = range(3)
 
+(CONSOLE_DISPLAY_WINDOWED,
+ CONSOLE_DISPLAY_UNDECORATED_WINDOW,
+ CONSOLE_DISPLAY_FULLSCREEN) = range(3)
 
 class vmmVMWindow(vmmGObjectUI):
     __gsignals__ = {
@@ -93,6 +96,7 @@ def __init__(self, vm, parent=None):
 
         self._shutdownmenu = None
         self._vmmenu = None
+        self._console_display_mode = CONSOLE_DISPLAY_WINDOWED
         self.init_menus()
 
         self.builder.connect_signals({
@@ -108,7 +112,7 @@ def __init__(self, vm, parent=None):
             "on_control_run_clicked": self.control_vm_run,
             "on_control_shutdown_clicked": self.control_vm_shutdown,
             "on_control_pause_toggled": self.control_vm_pause,
-            "on_control_fullscreen_toggled": self.control_fullscreen,
+            "on_control_fullscreen_toggled": self._toolbar_fullscreen_changed_cb,
 
             "on_details_customize_finish_clicked": self.customize_finish,
             "on_details_cancel_customize_clicked": self._customize_cancel_clicked,
@@ -125,6 +129,7 @@ def __init__(self, vm, parent=None):
             "on_details_pages_switch_page": self._details_page_switch_cb,
 
             "on_details_menu_view_fullscreen_activate": self._fullscreen_changed_cb,
+            "on_details_menu_view_undecorated_window_activate": self._undecorated_window_changed_cb,
             "on_details_menu_view_size_to_vm_activate": self._size_to_vm_cb,
             "on_details_menu_view_scale_always_toggled": self._scaling_ui_changed_cb,
             "on_details_menu_view_scale_fullscreen_toggled": self._scaling_ui_changed_cb,
@@ -301,10 +306,6 @@ def window_resized(self, ignore, ignore2):
             return  # pragma: no cover
         self._window_size = self.topwin.get_size()
 
-    def control_fullscreen(self, src):
-        menu = self.widget("details-menu-view-fullscreen")
-        if src.get_active() != menu.get_active():
-            menu.set_active(src.get_active())
 
     def toggle_toolbar(self, src):
         if self.is_customize_dialog:
@@ -312,7 +313,7 @@ def toggle_toolbar(self, src):
 
         active = src.get_active()
         self.config.set_details_show_toolbar(active)
-        fsactive = self.widget("details-menu-view-fullscreen").get_active()
+        fsactive = self.widget("details-menu-view-fullscreen").get_active() or self.widget("details-menu-view-undecorated-window").get_active()
         self.widget("toolbar-box").set_visible(active and not fsactive)
 
     def details_console_changed(self, src):
@@ -620,6 +621,8 @@ def _console_refresh_can_fullscreen(self):
         self.widget("control-fullscreen").set_sensitive(allow_fullscreen)
         self.widget("details-menu-view-fullscreen").set_sensitive(
             allow_fullscreen)
+        self.widget("details-menu-view-undecorated-window").set_sensitive(
+            allow_fullscreen)
 
     def _console_refresh_scaling_from_settings(self):
         scale_type = self.vm.get_console_scaling()
@@ -647,23 +650,73 @@ def _scaling_ui_changed_cb(self, src):
 
         self.vm.set_console_scaling(scale_type)
 
+    def _toolbar_fullscreen_changed_cb(self, src):
+        if src.get_active():
+            
+            if self._console_display_mode == CONSOLE_DISPLAY_WINDOWED:
+                # toggled in the toolbar, we switch to full screen mode by
+                # activating the "fullscreen" menu item
+
+                self.widget('details-menu-view-undecorated-window').set_active(False)
+                self.widget('details-menu-view-fullscreen').set_active(True)
+            else:
+                # the toolbar button was activated in fullscreen/undecorated
+                # window mode, no need to do anything
+                pass
+        else:
+        
+            if self._console_display_mode != CONSOLE_DISPLAY_WINDOWED:
+                # we left full-screen mode because of the "overlaid" exit
+                # button, which deactivates the toolbar button, let's deactivate
+                # the related menu elements as well.
+                
+                self.widget('details-menu-view-undecorated-window').set_active(False)
+                self.widget('details-menu-view-fullscreen').set_active(False)
+            else:
+                # the toolbar button cannot be deactivated in windowed mode
+                # (as it is not visible), unless programatically, let's not do anything
+                pass
+
     def _fullscreen_changed_cb(self, src):
-        do_fullscreen = src.get_active()
-        self.widget("control-fullscreen").set_active(do_fullscreen)
+        if src.get_active():
+            self._toggle_display_mode(CONSOLE_DISPLAY_FULLSCREEN)
+        else:
+            self._toggle_display_mode(CONSOLE_DISPLAY_WINDOWED)
 
-        if do_fullscreen:
-            self.topwin.fullscreen()
+    def _undecorated_window_changed_cb(self, src):
+        if src.get_active():
+            self._toggle_display_mode(CONSOLE_DISPLAY_UNDECORATED_WINDOW)
         else:
+            self._toggle_display_mode(CONSOLE_DISPLAY_WINDOWED)
+    
+    def _toggle_display_mode(self, mode):
+        print("toggle_display_mode", mode, 'previous mode', self._console_display_mode)
+    
+        if self._console_display_mode == mode:
+            return
+        decoration_needed = (mode == CONSOLE_DISPLAY_WINDOWED)
+        console_in_fullscreen = (mode != CONSOLE_DISPLAY_WINDOWED)
+
+        
+        if mode == CONSOLE_DISPLAY_FULLSCREEN:
+            self.topwin.fullscreen()
+        elif mode == CONSOLE_DISPLAY_WINDOWED:
             self.topwin.unfullscreen()
+            self.topwin.set_decorated(True)
+        elif mode == CONSOLE_DISPLAY_UNDECORATED_WINDOW:
+            self.topwin.set_decorated(False)
 
-        self._console.vmwindow_set_fullscreen(do_fullscreen)
+        self._console.vmwindow_set_fullscreen(console_in_fullscreen)
 
-        self.widget("details-menubar").set_visible(not do_fullscreen)
+        self.widget("details-menubar").set_visible(decoration_needed)
 
-        show_toolbar = not do_fullscreen
+        show_toolbar = decoration_needed
         if not self.widget("details-menu-view-toolbar").get_active():
             show_toolbar = False  # pragma: no cover
         self.widget("toolbar-box").set_visible(show_toolbar)
+        self._console_display_mode = mode
+        self.widget("control-fullscreen").set_active(console_in_fullscreen)
+        
 
     def _resizeguest_ui_changed_cb(self, src):
         if not src.get_sensitive():
